package com.ng.common.component.engine.parser;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import com.ng.common.component.engine.dto.Token;
import com.ng.common.component.engine.enums.TokenType;

/**
 * 数学表达式解析器
 * 支持基本的四则运算和变量替换
 * 线程安全实现
 */
public class ExpressionParser {
    /** 运算符优先级映射 */
    private static final Map<String, Integer> precedence;
    /** 数值计算精度控制 */
    private static final MathContext MC = new MathContext(6, RoundingMode.HALF_UP);
    /** 初始容量常量，用于优化集合初始化 */
    private static final int INITIAL_CAPACITY = 16;
    
    // 静态初始化运算符优先级
    static {
        Map<String, Integer> map = new ConcurrentHashMap<>(8);
        map.put("+", 1); // 加法优先级
        map.put("-", 1); // 减法优先级
        map.put("*", 2); // 乘法优先级
        map.put("/", 2); // 除法优先级
        map.put("(", 0); // 左括号优先级最低
        precedence = Collections.unmodifiableMap(map); // 使map不可修改，保证线程安全
    }

    /**
     * 计算表达式的值
     * @param expression 要计算的表达式字符串
     * @param variables 变量映射表
     * @return 计算结果
     * @throws IllegalArgumentException 当表达式无效时抛出
     */
    public double evaluate(String expression, Map<String, Double> variables) {
        // 创建变量的线程安全副本
        Map<String, Double> localVars = new HashMap<>(variables);
        try {
            List<Token> tokens = tokenize(expression);
            List<String> postfix = infixToPostfix(tokens);
            return evaluatePostfix(postfix, localVars);
        } catch (Exception e) {
            throw new IllegalArgumentException("Expression evaluation failed: " + e.getMessage());
        }
    }

    /**
     * 将表达式字符串转换为Token列表
     * @param expression 表达式字符串
     * @return Token列表
     */
    private List<Token> tokenize(final String expression) {
        // 使用线程本地的StringBuilder以确保线程安全
        ThreadLocal<StringBuilder> localBuilder = ThreadLocal.withInitial(() -> 
            new StringBuilder(INITIAL_CAPACITY));
        
        List<Token> tokens = new ArrayList<>(INITIAL_CAPACITY);
        char[] chars = expression.toCharArray();
        int len = chars.length;
        StringBuilder builder = localBuilder.get();
        
        try {
            for (int i = 0; i < len; i++) {
                char c = chars[i];
                
                // 跳过空白字符
                if (Character.isWhitespace(c)) {
                    continue;
                }
                
                // 处理数字
                if (Character.isDigit(c) || c == '.') {
                    builder.setLength(0);
                    while (i < len && (Character.isDigit(chars[i]) || chars[i] == '.')) {
                        builder.append(chars[i++]);
                    }
                    i--;
                    tokens.add(new Token(TokenType.OPERAND, builder.toString()));
                } 
                // 处理变量名
                else if (Character.isLetter(c)) {
                    builder.setLength(0);
                    while (i < len && Character.isLetter(chars[i])) {
                        builder.append(chars[i++]);
                    }
                    i--;
                    tokens.add(new Token(TokenType.OPERAND, builder.toString()));
                } 
                // 处理运算符和括号
                else if ("+-*/()".indexOf(c) != -1) {
                    tokens.add(new Token(
                        c == '(' ? TokenType.LEFT_PAREN :
                        c == ')' ? TokenType.RIGHT_PAREN :
                        TokenType.OPERATOR,
                        String.valueOf(c)
                    ));
                }
            }
            return Collections.unmodifiableList(tokens);
        } finally {
            localBuilder.remove(); // 清理ThreadLocal，防止内存泄漏
        }
    }

    /**
     * 将中缀表达式转换为后缀表达式（逆波兰表达式）
     * @param tokens Token列表
     * @return 后缀表达式字符串列表
     */
    private List<String> infixToPostfix(List<Token> tokens) {
        List<String> output = new ArrayList<>(tokens.size());
        Deque<Token> stack = new ArrayDeque<>(tokens.size());

        for (Token token : tokens) {
            switch (token.getType()) {
                case OPERAND:
                    output.add(token.getValue());
                    break;
                case LEFT_PAREN:
                    stack.push(token);
                    break;
                case RIGHT_PAREN:
                    // 处理右括号：弹出运算符直到遇到左括号
                    while (!stack.isEmpty() && stack.peek().getType() != TokenType.LEFT_PAREN) {
                        output.add(stack.pop().getValue());
                    }
                    if (!stack.isEmpty()) {
                        stack.pop(); // 弹出左括号
                    }
                    break;
                case OPERATOR:
                    // 处理运算符：比较优先级
                    while (!stack.isEmpty() && stack.peek().getType() == TokenType.OPERATOR) {
                        int prec1 = precedence.get(token.getValue());
                        int prec2 = precedence.get(stack.peek().getValue());
                        if (prec1 <= prec2) {
                            output.add(stack.pop().getValue());
                        } else {
                            break;
                        }
                    }
                    stack.push(token);
                    break;
            }
        }

        // 处理栈中剩余的运算符
        while (!stack.isEmpty()) {
            Token token = stack.pop();
            if (token.getType() != TokenType.LEFT_PAREN) {
                output.add(token.getValue());
            }
        }

        return Collections.unmodifiableList(output);
    }

    /**
     * 计算后缀表达式的值
     * @param postfix 后缀表达式字符串列表
     * @param variables 变量映射表
     * @return 计算结果
     * @throws IllegalArgumentException 当表达式无效或计算出错时抛出
     */
    private double evaluatePostfix(List<String> postfix, Map<String, Double> variables) {
        Deque<BigDecimal> stack = new ArrayDeque<>(postfix.size());

        for (String token : postfix) {
            // 处理数字
            if (token.matches("-?\\d+(\\.\\d+)?")) {
                stack.push(new BigDecimal(token));
            } 
            // 处理变量
            else if (variables.containsKey(token)) {
                stack.push(BigDecimal.valueOf(variables.get(token)));
            } 
            // 处理运算符
            else if ("+-*/".contains(token)) {
                if (stack.size() < 2) {
                    throw new IllegalArgumentException("Invalid expression");
                }
                BigDecimal b = stack.pop();
                BigDecimal a = stack.pop();
                
                BigDecimal result;
                switch (token.charAt(0)) {
                    case '+':
                        result = a.add(b);
                        break;
                    case '-':
                        result = a.subtract(b);
                        break;
                    case '*':
                        result = a.multiply(b);
                        break;
                    case '/':
                        if (b.compareTo(BigDecimal.ZERO) == 0) {
                            throw new IllegalArgumentException("Division by zero");
                        }
                        // 除法运算保留6位小数，使用四舍五入
                        result = a.divide(b, MC).stripTrailingZeros();
                        break;
                    default:
                        throw new IllegalArgumentException("Unknown operator: " + token);
                }
                stack.push(result);
            } else {
                throw new IllegalArgumentException("Unknown token: " + token);
            }
        }

        return stack.pop().doubleValue();
    }
    
    public static void main(String[] args) {
        try {
            ExpressionParser parser = new ExpressionParser();

            // 示例表达式
            String expression = "(a + b * c) / d - e";
            Map<String, Double> variables = new HashMap<>();
            variables.put("a", 2.0);
            variables.put("b", 3.0);
            variables.put("c", 4.0);
            variables.put("d", 5.0);
            variables.put("e", 1.0);

            double result = parser.evaluate(expression, variables);
            System.out.println("Result: " + result); // 输出结果
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
